package service

import (
	"errors"
	"fitness/go-admin/internal/model"
	"fitness/go-admin/internal/repository"
	"fitness/go-admin/pkg/errcode"
	"fitness/go-admin/pkg/logger"
	"time"

	"gorm.io/gorm"
)

type BookingService interface {
	Create(userID uint, req *model.BookingCreateReq) (*model.BookingCreateResp, error)
	Cancel(userID uint, bookingID uint, req *model.BookingCancelReq) error
	MyBookings(userID uint, req *model.BookingListReq) ([]model.BookingListItem, int64, error)
	GetByID(userID uint, bookingID uint, isAdmin bool) (*model.BookingDetailResp, error)
	CheckIn(userID uint, bookingID uint, isAdmin bool) (*model.CheckInResp, error)
	ListByCourse(courseID uint, userID uint, isAdmin bool, req *model.BookingListReq) ([]model.BookingListItem, int64, error)
	AdminList(req *model.BookingListReq) ([]model.BookingListItem, int64, error)
	AdminCancel(bookingID uint, req *model.BookingCancelReq, adminID uint) error
}

type bookingService struct {
	bookingRepo repository.BookingRepository
	courseRepo  repository.CourseRepository
	memberRepo  repository.MemberRepository
	userRepo    repository.UserRepository
}

func NewBookingService(
	bookingRepo repository.BookingRepository,
	courseRepo repository.CourseRepository,
	memberRepo repository.MemberRepository,
	userRepo repository.UserRepository,
) BookingService {
	return &bookingService{
		bookingRepo: bookingRepo,
		courseRepo:  courseRepo,
		memberRepo:  memberRepo,
		userRepo:    userRepo,
	}
}

// Create 创建预约
func (s *bookingService) Create(userID uint, req *model.BookingCreateReq) (*model.BookingCreateResp, error) {
	// 1. 检查课程是否存在
	course, err := s.courseRepo.GetByID(req.CourseID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errcode.NewError(errcode.CourseNotFound)
		}
		return nil, err
	}

	// 2. 检查课程状态（只能预约可用课程）
	if course.Status != 1 {
		return nil, errcode.NewError(errcode.CourseNotAvailable)
	}

	// 3. 检查课程是否已开始
	now := time.Now()
	if course.StartTime.Before(now) {
		return nil, errcode.NewError(errcode.CourseAlreadyStarted)
	}

	// 4. 确定会员ID（如果未提供，使用当前用户关联的会员）
	memberID := req.MemberID
	if memberID == 0 {
		// 查找用户关联的会员（简化处理，假设用户ID和会员ID一致，实际应该有关联表）
		member, err := s.memberRepo.GetByID(userID)
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil, errcode.NewError(errcode.MemberNotFound)
			}
			return nil, err
		}
		memberID = member.ID
	} else {
		// 验证会员是否存在
		_, err := s.memberRepo.GetByID(memberID)
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil, errcode.NewError(errcode.MemberNotFound)
			}
			return nil, err
		}
	}

	// 5. 检查是否已预约该课程
	existing, err := s.bookingRepo.GetByUserAndCourse(userID, req.CourseID)
	if err == nil && existing.ID > 0 && existing.Status == model.BookingStatusBooked {
		return nil, errcode.NewError(errcode.BookingExists)
	}

	// 6. 检查课程容量
	bookedCount, err := s.bookingRepo.CountByCourse(req.CourseID, model.BookingStatusBooked)
	if err != nil {
		return nil, err
	}
	if bookedCount >= int64(course.MaxCapacity) {
		return nil, errcode.NewError(errcode.CourseFullyBooked)
	}

	// 7. 检查时间冲突
	conflict, err := s.bookingRepo.CheckTimeConflict(userID, course.StartTime, course.EndTime, 0)
	if err != nil {
		return nil, err
	}
	if conflict {
		return nil, errcode.NewError(errcode.BookingTimeConflict)
	}

	// 8. 创建预约
	booking := &model.CourseBooking{
		CourseID: req.CourseID,
		MemberID: memberID,
		UserID:   userID,
		Status:   model.BookingStatusBooked,
		BookedAt: now,
	}

	if err := s.bookingRepo.Create(booking); err != nil {
		return nil, err
	}

	// 9. 记录审计日志
	user, _ := s.userRepo.GetByID(userID)
	member, _ := s.memberRepo.GetByID(memberID)
	logger.LogAudit(logger.AuditLog{
		UserID:    userID,
		Username:  user.Username,
		Action:    "BOOKING_CREATE",
		Resource:  "booking",
		IP:        "",
		UserAgent: "",
	})

	// 10. 返回创建结果
	return &model.BookingCreateResp{
		ID:         booking.ID,
		CourseID:   course.ID,
		CourseName: course.Name,
		CourseTime: course.StartTime,
		MemberID:   memberID,
		MemberName: member.Name,
		Status:     booking.Status,
		StatusText: model.GetBookingStatusText(booking.Status),
		BookedAt:   booking.BookedAt,
	}, nil
}

// Cancel 取消预约
func (s *bookingService) Cancel(userID uint, bookingID uint, req *model.BookingCancelReq) error {
	// 1. 获取预约信息
	booking, err := s.bookingRepo.GetByID(bookingID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errcode.NewError(errcode.BookingNotFound)
		}
		return err
	}

	// 2. 权限检查：只能取消自己的预约
	if booking.UserID != userID {
		return errcode.NewError(errcode.Forbidden)
	}

	// 3. 状态检查：已取消或已签到的预约不能取消
	if booking.Status == model.BookingStatusCancelled {
		return errcode.NewError(errcode.BookingCancelled)
	}
	if booking.Status == model.BookingStatusCheckedIn || booking.Status == model.BookingStatusCompleted {
		return errcode.NewError(errcode.CannotCancelBooking)
	}

	// 4. 获取课程信息
	course, err := s.courseRepo.GetByID(booking.CourseID)
	if err != nil {
		return err
	}

	// 5. 时间检查：课程开始前2小时禁止取消
	now := time.Now()
	twoHoursBeforeStart := course.StartTime.Add(-2 * time.Hour)
	if now.After(twoHoursBeforeStart) {
		return errcode.NewError(errcode.CannotCancelBooking)
	}

	// 6. 更新预约状态
	cancelTime := now
	booking.Status = model.BookingStatusCancelled
	booking.CancelledAt = &cancelTime
	booking.CancelReason = req.Reason

	if err := s.bookingRepo.Update(booking); err != nil {
		return err
	}

	// 7. 记录审计日志
	user, _ := s.userRepo.GetByID(userID)
	logger.LogAudit(logger.AuditLog{
		UserID:    userID,
		Username:  user.Username,
		Action:    "BOOKING_CANCEL",
		Resource:  "booking",
		IP:        "",
		UserAgent: "",
	})

	return nil
}

// MyBookings 我的预约列表
func (s *bookingService) MyBookings(userID uint, req *model.BookingListReq) ([]model.BookingListItem, int64, error) {
	return s.bookingRepo.ListByUser(userID, req)
}

// GetByID 获取预约详情
func (s *bookingService) GetByID(userID uint, bookingID uint, isAdmin bool) (*model.BookingDetailResp, error) {
	// 1. 获取预约信息
	booking, err := s.bookingRepo.GetByID(bookingID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errcode.NewError(errcode.BookingNotFound)
		}
		return nil, err
	}

	// 2. 权限检查：普通用户只能查看自己的预约
	if !isAdmin && booking.UserID != userID {
		return nil, errcode.NewError(errcode.Forbidden)
	}

	// 3. 构建响应
	resp := &model.BookingDetailResp{
		ID:           booking.ID,
		CourseID:     booking.CourseID,
		MemberID:     booking.MemberID,
		UserID:       booking.UserID,
		Status:       booking.Status,
		StatusText:   model.GetBookingStatusText(booking.Status),
		BookedAt:     booking.BookedAt,
		CancelledAt:  booking.CancelledAt,
		CheckInAt:    booking.CheckInAt,
		CancelReason: booking.CancelReason,
		CreatedAt:    booking.CreatedAt,
		UpdatedAt:    booking.UpdatedAt,
	}

	// 4. 填充关联信息
	if booking.Course != nil {
		resp.CourseName = booking.Course.Name
		resp.CourseTime = booking.Course.StartTime
		resp.CourseDuration = booking.Course.Duration
		// 获取教练名称（需要关联查询）
		// TODO: 添加教练名称
	}
	if booking.Member != nil {
		resp.MemberName = booking.Member.Name
	}
	if booking.User != nil {
		resp.Username = booking.User.Username
	}

	return resp, nil
}

// CheckIn 课程签到
func (s *bookingService) CheckIn(userID uint, bookingID uint, isAdmin bool) (*model.CheckInResp, error) {
	// 1. 获取预约信息
	booking, err := s.bookingRepo.GetByID(bookingID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errcode.NewError(errcode.BookingNotFound)
		}
		return nil, err
	}

	// 2. 权限检查：普通用户只能签到自己的预约
	if !isAdmin && booking.UserID != userID {
		return nil, errcode.NewError(errcode.Forbidden)
	}

	// 3. 状态检查：只有"已预约"状态才能签到
	if booking.Status == model.BookingStatusCancelled {
		return nil, errcode.NewError(errcode.BookingCancelled)
	}
	if booking.Status == model.BookingStatusCheckedIn {
		return nil, errcode.NewError(errcode.AlreadyCheckedIn)
	}
	if booking.Status != model.BookingStatusBooked {
		return nil, errcode.NewError(errcode.CannotCheckIn)
	}

	// 4. 获取课程信息
	course, err := s.courseRepo.GetByID(booking.CourseID)
	if err != nil {
		return nil, err
	}

	// 5. 时间检查：课程开始前30分钟~课程结束
	now := time.Now()
	thirtyMinutesBeforeStart := course.StartTime.Add(-30 * time.Minute)

	if now.Before(thirtyMinutesBeforeStart) {
		return nil, errcode.NewError(errcode.CheckInTimeNotReady)
	}
	if now.After(course.EndTime) {
		return nil, errcode.NewError(errcode.CourseAlreadyStarted)
	}

	// 6. 更新签到状态
	checkInTime := now
	booking.Status = model.BookingStatusCheckedIn
	booking.CheckInAt = &checkInTime

	if err := s.bookingRepo.Update(booking); err != nil {
		return nil, err
	}

	// 7. 记录审计日志
	user, _ := s.userRepo.GetByID(userID)
	logger.LogAudit(logger.AuditLog{
		UserID:    userID,
		Username:  user.Username,
		Action:    "BOOKING_CHECKIN",
		Resource:  "booking",
		IP:        "",
		UserAgent: "",
	})

	return &model.CheckInResp{
		ID:         booking.ID,
		Status:     booking.Status,
		StatusText: model.GetBookingStatusText(booking.Status),
		CheckInAt:  *booking.CheckInAt,
	}, nil
}

// ListByCourse 获取课程的预约列表
func (s *bookingService) ListByCourse(courseID uint, userID uint, isAdmin bool, req *model.BookingListReq) ([]model.BookingListItem, int64, error) {
	// 1. 检查课程是否存在
	course, err := s.courseRepo.GetByID(courseID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, 0, errcode.NewError(errcode.CourseNotFound)
		}
		return nil, 0, err
	}

	// 2. 权限检查：教练只能查看自己的课程（TODO: 需要添加教练与用户关联）
	if !isAdmin {
		// 简化处理：暂时允许所有人查看
		// 实际应该检查 course.CoachID 是否等于当前用户关联的教练ID
		_ = course
	}

	// 3. 查询预约列表
	return s.bookingRepo.ListByCourse(courseID, req)
}

// AdminList 管理员查看所有预约
func (s *bookingService) AdminList(req *model.BookingListReq) ([]model.BookingListItem, int64, error) {
	return s.bookingRepo.List(req)
}

// AdminCancel 管理员取消预约
func (s *bookingService) AdminCancel(bookingID uint, req *model.BookingCancelReq, adminID uint) error {
	// 1. 获取预约信息
	booking, err := s.bookingRepo.GetByID(bookingID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errcode.NewError(errcode.BookingNotFound)
		}
		return err
	}

	// 2. 状态检查：已取消的预约不能再次取消
	if booking.Status == model.BookingStatusCancelled {
		return errcode.NewError(errcode.BookingCancelled)
	}

	// 3. 更新预约状态（管理员可以取消任何状态的预约）
	now := time.Now()
	booking.Status = model.BookingStatusCancelled
	booking.CancelledAt = &now
	booking.CancelReason = req.Reason

	if err := s.bookingRepo.Update(booking); err != nil {
		return err
	}

	// 4. 记录审计日志
	admin, _ := s.userRepo.GetByID(adminID)
	logger.LogAudit(logger.AuditLog{
		UserID:    adminID,
		Username:  admin.Username,
		Action:    "ADMIN_BOOKING_CANCEL",
		Resource:  "booking",
		IP:        "",
		UserAgent: "",
	})

	return nil
}
